
#include <cstdlib>

#include <QtOpenGL>

#include "entity/entity.h"
#include "object/face.h"
#include "object/material.h"
#include "object/object.h"

Entity::Entity()
    : m_pkObject(NULL)
{
}

Entity::Entity(Object& rkObject)
    : m_pkObject(&rkObject)
{
}

Entity::~Entity()
{
}

const QMatrix4x4& Entity::matrix() const
{
    return m_kMatrix;
}

void Entity::setMatrix(const QMatrix4x4& rkMatrix)
{
    m_kMatrix = rkMatrix;
}

void Entity::paint() const
{
    // Object is invalid, no painting required.
    if(!m_pkObject || !m_pkObject->isValid())
        return;

    // Set corresponding matrix.
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();

    glMultMatrixd(m_kMatrix.data());

    // Batching is done by material.
    Material* pkCurrentMaterial = NULL;
    foreach(const Face* pkCurrentFace, m_pkObject->faces())
    {
        // Skip invalid faces.
        if(!pkCurrentFace->isValid())
            continue;

        // Skip bindings if the material is NULL or invalid.
        if(pkCurrentFace->material() && pkCurrentFace->material()->isValid())
        {
            // Check if face material is already binded.
            if(!pkCurrentMaterial || pkCurrentMaterial != pkCurrentFace->material())
            {
                pkCurrentMaterial = pkCurrentFace->material();
                pkCurrentMaterial->bind();
            }
        }

        // Call GL painting functions.
        switch(pkCurrentFace->vertices().count())
        {
        case 3:
            // Triangle.
            glBegin(GL_TRIANGLES);
            break;

        case 4:
            // Quad.
            glBegin(GL_QUADS);
            break;

        default:
            // Invalid primitive.
            Q_ASSERT(false);
        }

        const QVector< const QVector3D* > kVertices = pkCurrentFace->vertices();
        const QVector< const QVector3D* > kNormals  = pkCurrentFace->normals();
        const QVector< const QVector2D* > kUVs      = pkCurrentFace->UVs();

        for(quint8 j = 0 ; j < pkCurrentFace->vertices().count(); ++j)
        {
            if(kNormals.count())
                glNormal3f(kNormals.at(j)->x(), kNormals.at(j)->y(), kNormals.at(j)->z());

            if(kUVs.count())
                glTexCoord2f(kUVs.at(j)->x(), kUVs.at(j)->y());

            glVertex3f(kVertices.at(j)->x(), kVertices.at(j)->y(), kVertices.at(j)->z());
        }

        glEnd();
    }

    // Unbind material.
    if(pkCurrentMaterial)
    {
        pkCurrentMaterial->unbind();
    }

    // Popping.
    glPopMatrix();
}

void Entity::setObject(Object& rkObject)
{
    m_pkObject = &rkObject;
}
